package internal

import (
	"encoding/json"
	"errors"
	"fmt"
	"gorm.io/gorm"
	"k9-panel/app/model"
	"k9-panel/app/query"
	"k9-panel/app/service/dto"
	"k9-panel/app/service/dto/convert"
	"strings"
)

type DerivedConfigService struct {
}

func (receiver *DerivedConfigService) UpdateByParam(param *dto.DerivedAddParam) (uint, error) {
	//TODO implement me
	_, err := receiver.Update(param.ProjectId, param.EnvironmentId, param.Content)
	if err != nil {
		return 0, err
	}
	diIds, err := json.Marshal(param.DependencyProjectIds)
	if err != nil {
		return 0, err
	}

	first, err := query.ProjectEnvironment.
		Where(query.ProjectEnvironment.ProjectId.Eq(param.ProjectId)).
		Where(query.ProjectEnvironment.EnvironmentId.Eq(param.EnvironmentId)).First()
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return 0, err
	}
	if first == nil {
		pe := &model.ProjectEnvironment{
			EnvironmentId:        param.EnvironmentId,
			ProjectId:            param.ProjectId,
			DependencyProjectIds: string(diIds),
		}
		err = query.ProjectEnvironment.Create(pe)
	} else {
		_, err := query.ProjectEnvironment.Where(query.ProjectEnvironment.ID.Eq(first.ID)).Update(query.ProjectEnvironment.DependencyProjectIds, diIds)
		if err != nil {
			return 0, err
		}
	}
	return 1, nil
}

func (receiver *DerivedConfigService) ViewConfigEnv(id uint, env *model.Environment) (*dto.ConfigEnv, error) {
	//TODO implement me

	cfgEnv, err2 := receiver.getDerivedInfo(id, env)
	if err2 != nil {
		return nil, err2
	}
	info := &dto.ProjectBaseConfigInfo{}

	projectEnvironment, err := query.ProjectEnvironment.
		Where(query.ProjectEnvironment.EnvironmentId.Eq(env.ID)).
		Where(query.ProjectEnvironment.ProjectId.Eq(id)).First()
	if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
		return nil, err
	}
	if projectEnvironment != nil && len(projectEnvironment.DependencyProjectIds) > 0 {
		err := json.Unmarshal([]byte(projectEnvironment.DependencyProjectIds), &info.DependencyProjectIds)
		if err != nil {
			return nil, err
		}

		for _, pId := range info.DependencyProjectIds {
			projectFilter := &model.Project{}
			projectFilter.ID = *pId
			projectFilter, err := svc.Project.Get(projectFilter)
			if err != nil {
				return nil, err
			}

			derivedInfo, err := receiver.getDerivedInfo(*pId, env)
			if err != nil {
				return nil, err
			}
			cfgEnv, err = cfgEnv.Merge(derivedInfo)
			if err != nil {
				return nil, err
			}

		}
	}

	return cfgEnv, nil
}
func (receiver *DerivedConfigService) ViewProject(id uint, envId uint) (*dto.ProjectBaseConfigInfo, error) {
	//TODO implement me
	env, err := svc.Environment.Get(envId)

	cfgEnv, err2 := receiver.getDerivedInfo(id, env)
	if err2 != nil {
		return nil, err2
	}
	info := &dto.ProjectBaseConfigInfo{}
	project := model.Project{}
	project.ID = id
	p, err := svc.Project.Get(&project)
	if err != nil {
		return nil, err
	}

	projectEnvironment, err := query.ProjectEnvironment.
		Where(query.ProjectEnvironment.EnvironmentId.Eq(envId)).
		Where(query.ProjectEnvironment.ProjectId.Eq(id)).First()
	if err != nil {
		return nil, err
	}
	if len(projectEnvironment.DependencyProjectIds) > 0 {
		err := json.Unmarshal([]byte(projectEnvironment.DependencyProjectIds), &info.DependencyProjectIds)
		if err != nil {
			return nil, err
		}

		for _, pId := range info.DependencyProjectIds {
			projectFilter := &model.Project{}
			projectFilter.ID = *pId
			projectFilter, err := svc.Project.Get(projectFilter)
			if err != nil {
				return nil, err
			}

			derivedInfo, err := receiver.ViewConfigEnv(*pId, env)
			if errors.Is(gorm.ErrRecordNotFound, err) {
				continue
			}
			if err != nil {
				return nil, err
			}
			cfgEnv, err = cfgEnv.Merge(derivedInfo)
			if err != nil {
				return nil, err
			}

		}
	}

	info.Project = &dto.ProjectData{Id: p.ID, Name: p.Name}
	info.Content, _ = cfgEnv.ToString()
	return info, nil
}

func (receiver *DerivedConfigService) StringToConfig(content string) ([]*model.DerivedConfig, error) {
	var (
		batch []*model.DerivedConfig
	)
	configs := strings.Split(strings.TrimSpace(content), "\n")

	for idx, config := range configs {
		config := strings.TrimSpace(config)
		if strings.HasPrefix(config, "##") {
			continue
		}
		if len(config) == 0 {
			continue
		}
		if strings.HasPrefix(config, "#") && !strings.Contains(config, "##") {
			config += "=1"
		}
		configPair := strings.Split(config, "=")
		if len(configPair) < 2 {
			return nil, errors.New("配置格式异常,附近位置:" + config)
		}
		baseConfig := &model.DerivedConfig{}
		baseConfig.Key = configPair[0]
		baseConfig.Value = strings.Join(configPair[1:], "=")
		baseConfig.Sort = idx
		batch = append(batch, baseConfig)
	}
	return batch, nil
}
func (receiver *DerivedConfigService) ConfigToString(configs map[string]interface{}) (content string, err error) {
	for k, v := range configs {
		if strings.HasPrefix(k, "##") {
			content += fmt.Sprintf("%s %s\n", k, v) + "\n"
		} else {
			content += k + "=" + fmt.Sprintf("%s", v) + "\n"
		}
	}
	return content, nil
}
func (receiver *DerivedConfigService) Create(projectId uint, envId uint, content string) (uint, error) {

	if projectId == 0 {
		return 0, errors.New("项目ID不能为空！")
	}
	configs, err := receiver.StringToConfig(content)
	if err != nil {
		return 0, err
	}
	for idx, config := range configs {
		config.ProjectId = int(projectId)
		config.EnvironmentId = int(envId)
		config.Sort = idx
	}
	err = query.DerivedConfig.Create(configs...)
	if err != nil {
		return 0, err
	}
	return 1, nil
}

func (receiver *DerivedConfigService) DeleteByProject(id int) (int64, error) {
	res, err := query.DerivedConfig.Where(query.DerivedConfig.ProjectId.Eq(id)).Delete()

	if err != nil {
		return 0, err
	}
	return res.RowsAffected, nil
}
func MergeMap(a1 map[string]interface{}, b1 map[string]interface{}) (map[string]interface{}, error) {
	if a1 == nil || b1 == nil {
		return nil, fmt.Errorf("input maps cannot be nil")
	}

	// 创建一个新的 map 来避免修改输入参数
	result := make(map[string]interface{})
	for k, v := range b1 {
		result[k] = v
	}

	for k, v := range a1 {
		result[k] = v // 如果需要避免覆盖，可以在这里添加逻辑
	}

	return result, nil
}
func (receiver *DerivedConfigService) GetByProject(id uint, envId uint) (*dto.ProjectBaseConfigInfo, error) {
	env, err := svc.Environment.Get(envId)
	if err != nil {
		return nil, err
	}
	cfgEnv, err2 := receiver.getDerivedInfo(id, env)
	if err2 != nil {
		return nil, err2
	}
	info := &dto.ProjectBaseConfigInfo{}
	filterProject := model.Project{}
	filterProject.ID = id
	project, err := svc.Project.Get(&filterProject)
	if err != nil {
		return nil, err
	}
	projectEnvironment, err := query.ProjectEnvironment.
		Where(query.ProjectEnvironment.EnvironmentId.Eq(envId)).
		Where(query.ProjectEnvironment.ProjectId.Eq(id)).First()
	if err != nil {
		return nil, err
	}
	if len(projectEnvironment.DependencyProjectIds) > 0 {
		err := json.Unmarshal([]byte(projectEnvironment.DependencyProjectIds), &info.DependencyProjectIds)
		if err != nil {
			return nil, err
		}
	}

	info.Project = &dto.ProjectData{Id: project.ID, Name: project.Name}
	info.Content, _ = cfgEnv.ToString()
	return info, nil
}

func (receiver *DerivedConfigService) getDerivedInfo(id uint, env *model.Environment) (*dto.ConfigEnv, error) {
	extendConfig, err := query.DerivedConfig.
		Preload(query.DerivedConfig.Project).
		Preload(query.DerivedConfig.Environment).
		Where(query.DerivedConfig.ProjectId.Eq(int(id))).
		Where(query.DerivedConfig.EnvironmentId.Eq(int(env.ID))).
		Order(query.DerivedConfig.Sort.Asc()).
		Find()
	if err != nil {
		return nil, err
	}
	baseConfig, err := svc.BaseConfig.GetListByProject(id)
	if err != nil {
		return nil, err
	}

	proj, err := svc.Project.Get(&model.Project{
		Model: model.Model{ID: id},
	})
	if err != nil {
		return nil, err
	}
	baseConfigEnv, err := svc.BaseConfig.Convert2ConfigEnv(proj.Name+"环境:全局", baseConfig)
	if err != nil {
		return nil, err
	}
	if extendConfig == nil || len(extendConfig) == 0 {
		return baseConfigEnv, nil
	}
	configEnv := convert.DerivedConfig2ConfigEnv(proj.Name+"环境："+env.Name, extendConfig)
	return configEnv.MergeBeforeMe(baseConfigEnv)
}
func (receiver *DerivedConfigService) Update(projectId uint, envId uint, content string) (uint, error) {
	var (
		deletes []uint
		updates []*model.DerivedConfig
		addList []*model.DerivedConfig
	)
	hasOne, err := query.DerivedConfig.
		Where(query.DerivedConfig.ProjectId.Eq(int(projectId))).
		Where(query.DerivedConfig.EnvironmentId.Eq(int(envId))).
		First()
	if err != nil || hasOne == nil {
		return receiver.Create(projectId, envId, content)
	}
	dbResp, err := query.DerivedConfig.
		Where(query.DerivedConfig.ProjectId.Eq(int(projectId))).
		Where(query.DerivedConfig.EnvironmentId.Eq(int(envId))).
		Find()
	dbBaseResp, err := query.BaseConfig.Where(query.BaseConfig.ProjectId.Eq(int(projectId))).Find()
	if err != nil {
		return 0, err
	}
	for _, config := range dbBaseResp {
		dbResp = append(dbResp, &model.DerivedConfig{Key: config.Key, Value: config.Value})
	}
	cfg, err := convert.StringToConfigEnv(content)
	if err != nil {
		return 0, err
	}
	updateFilter := make(map[string]string, len(cfg.Items))
	dbFilter := make(map[string]interface{}, len(dbResp))

	for _, config := range cfg.Items {
		updateFilter[config.Key] = config.Value
	}

	for _, resItem := range dbResp {
		dbFilter[resItem.Key] = resItem.Value

		if _, ok := updateFilter[resItem.Key]; ok {
			if updateFilter[resItem.Key] != resItem.Value {
				resItem.Value = updateFilter[resItem.Key]

				updates = append(updates, resItem)
			}
		} else {
			deletes = append(deletes, resItem.ID)
		}
	}
	for _, update := range cfg.Items {
		if dbFilter[update.Key] == nil {
			model1 := &model.DerivedConfig{}
			model1.Key = update.Key
			model1.Value = update.Value
			model1.Sort = update.Sort
			model1.ProjectId = int(projectId)
			model1.EnvironmentId = int(envId)
			addList = append(addList, model1)
		}
	}

	err = query.DerivedConfig.Create(addList...)
	if err != nil {
		return 0, err
	}
	if len(deletes) > 0 {
		_, err = query.DerivedConfig.Where(query.DerivedConfig.ID.In(deletes...)).Delete()
		if err != nil {
			return 0, err
		}
	}

	for _, updateItem := range updates {
		_, err := query.DerivedConfig.Where(query.DerivedConfig.ID.Eq(updateItem.ID)).Update(query.DerivedConfig.Value, updateItem.Value)
		if err != nil {
			return 0, err
		}

	}
	return 1, nil
}
